﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MiniCPU.Entities
{
    /// <summary>
    /// ALU Operation Enumeration.
    /// </summary>
    public enum ALUOperation 
    {
        And, 
        Subtract, 
        Decrement 
    }

    /// <summary>
    /// ALU Entity.
    /// </summary>
    public class ALU : DataPropagator
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="ALU"/> class.
        /// </summary>
        /// <param name="inputA">The input A.</param>
        /// <param name="inputB">The input B.</param>
        /// <param name="numberOfBits">The number of bits.</param>
        public ALU(LimitedData inputA, LimitedData inputB, int numberOfBits)
            : base("ALU", 0, numberOfBits)
        {
            this.InputA = inputA;
            this.InputB = inputB;

            this.And = new ControlPort("ALU:And");
            this.Subtract = new ControlPort("ALU:Subtract");
            this.Decrement = new ControlPort("ALU:Decrement");

            this.InputA.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
            this.InputB.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);

            this.And.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
            this.Subtract.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
            this.Decrement.DataChanged += new MiniCPU.Events.DataChangedEventHandler(this.RefreshData);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets or sets the input A.
        /// </summary>
        /// <value>The input A.</value>
        public LimitedData InputA { get; set; }

        /// <summary>
        /// Gets or sets the input B.
        /// </summary>
        /// <value>The input B.</value>
        public LimitedData InputB { get; set; }

        /// <summary>
        /// Gets or sets the AND operation selector.
        /// </summary>
        /// <value>The AND operation selector.</value>
        public ControlPort And { get; set; }

        /// <summary>
        /// Gets or sets the Subtract operation selector.
        /// </summary>
        /// <value>The Subtract operation selector.</value>
        public ControlPort Subtract { get; set; }

        /// <summary>
        /// Gets or sets the Decrement operation selector.
        /// </summary>
        /// <value>The Decrement operation selector.</value>
        public ControlPort Decrement { get; set; }
        #endregion

        #region Methods
        /// <summary>
        /// Data Changed event handler.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="MiniCPU.Events.DataChangedEventArgs"/> instance containing the event data.</param>
        public void RefreshData(object sender, MiniCPU.Events.DataChangedEventArgs e)
        {
            this.UpdateALUOutput();
        }

        /// <summary>
        /// Updates the ALU output.
        /// </summary>
        private void UpdateALUOutput()
        {
            ALUOperation currentOperation = ALUOperation.And;
            if (this.And.Value)
            {
                currentOperation = ALUOperation.And;
            }
            else if (this.Subtract.Value)
            {
                currentOperation = ALUOperation.Subtract;
            }
            else if (this.Decrement.Value)
            {
                currentOperation = ALUOperation.Decrement;
            }

            switch (currentOperation)
            {
                case ALUOperation.And:
                    this.IntValue = this.InputA.IntValue & this.InputB.IntValue;
                    break;
                case ALUOperation.Subtract:
                    this.IntValue = this.InputA.SignedIntValue - this.InputB.SignedIntValue;
                    break;
                case ALUOperation.Decrement:
                    this.IntValue = this.InputA.IntValue - 1;
                    break;
            }
        }
        #endregion
    }
}
